<template>
    <div class="VirtualSwiper-container"
        @mouseenter="onMouseEnter"
        @mouseleave="onMouseLeave">

        <div class="img-wrap"
            :style="{
                transform,
                transition,
            }"
            @transitionend="onTransitionEnd">

            <div class="img-item">
                <div v-for="(item, index) in _data"
                    :key="index"
                    :style="{
                        position: 'absolute',
                        width: '100%',
                        height: '100%',
                        top: 0,
                        left: `${index * 100 + (
                            index === 0
                                ? left1 * 100
                                : left2 * 100
                        )}%`,
                    }">

                    <slot :data="item">
                        <img v-if="item.src"
                            :src="item.src"
                            alt=""
                            :style="{
                                display: 'block',
                                height: '100%',
                                width: '100%',
                                objectFit: 'cover',
                            }"
                            :title="item.desc ?? ''" />

                        <div v-else>没有`src`属性 请使用插槽自定义</div>
                    </slot>
                </div>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts" generic="T extends {
    src?: string
    [K: string]: any
}">
/**
 * 虚拟轮播图 超高性能 无缝轮播
 * 就算有再多的图片 也只会加载两张
 */


defineOptions({
    inheritAttrs: true,
    name: 'VirtualSwiper'
})
const props = withDefaults(
    defineProps<{
        data: T[]
        duration?: number
        autoPlay?: boolean
        needHoverPause?: boolean
        transitionMs?: number
    }>(),
    {
        autoPlay: true,
        needHoverPause: true,
        duration: 1000,
        transitionMs: 500,
    }
)


const curIndex = ref(0),
    timer = ref<number>(),

    /** 两张图轮流交换实现无缝虚拟轮播 */
    imgIndexArr = ref([curIndex.value, curIndex.value + 1]),
    /** 不停切换两张图 实现虚拟无缝轮播 */
    _data = computed(() => [
        props.data[imgIndexArr.value[0]],
        props.data[(imgIndexArr.value[1]) % props.data.length]]
    ),

    transformIndex = ref(0),
    transition = ref(`transform ${props.transitionMs}ms`),
    transform = computed(() => `translateX(-${transformIndex.value * 100}%)`)


/** ---------------------------------------------------------------------------
 * 初始化入口
 */
init()
function init() {
    play()
    bindEvent()
}


/** ---------------------------------------------------------------------------
 * 事件
 */
/** 需要修改位置的索引 当首次滚动后 把第一张图片继续放到最后 循环往复 */
let curChangeIndex = 0
const left1 = ref(0),
    left2 = ref(0)

function onTransitionEnd() {
    curIndex.value = (curIndex.value + 1) % props.data.length
    changeIndexByNext()
}


function onMouseEnter() {
    if (!props.needHoverPause) {
        return
    }
    stop()
}
function onMouseLeave() {
    if (!props.needHoverPause) {
        return
    }
    play()
}

/** 离开浏览器时 不会执行`transitionend` 所以要停止 */
function bindEvent() {
    window.addEventListener('visibilitychange', () => {
        document.hidden
            ? stop()
            : play()
    })
}


/** ---------------------------------------------------------------------------
 * 工具函数
 */
/** 下一张图的切换逻辑 */
function changeIndexByNext() {
    if (curChangeIndex === 0) {
        left1.value += 2
        curChangeIndex = 1

        const i = (imgIndexArr.value[0] + 2) % props.data.length
        imgIndexArr.value[0] = i
    }
    else if (curChangeIndex === 1) {
        curChangeIndex = 0
        /** 以下两种方式都能实现无缝轮播 但是下面的 可以避免位移的值无限变大 */
        // left2.value += 2
        resetPos()

        const i = (imgIndexArr.value[1] + 2) % props.data.length
        imgIndexArr.value[1] = i
    }
}

function resetPos() {
    transition.value = 'none'
    transformIndex.value = 0
    left1.value = 0
    left2.value = 0
}

function next() {
    setTransition()
    transformIndex.value++
}

function play() {
    if (props.data.length < 2 || !props.autoPlay) return

    stop()
    timer.value = window.setInterval(next, props.duration)
}
function stop() {
    timer.value && clearInterval(timer.value)
    timer.value = undefined
}

function setTransition() {
    transition.value = `transform ${props.transitionMs}ms`
}


</script>

<style lang="scss" scoped>
.VirtualSwiper-container {
    height: 100%;
    overflow: hidden;
}

.img-wrap {
    height: 100%;
}

.img-item {
    position: relative;
    height: 100%;
}
</style>
